#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include <mutex>

#include "lockGuard.hpp"
#include "thread.hpp"
#include "log.hpp"

const int g_thread_num = 3; // 默认线程个数

// 线程池其本质是生产消费者模型
template<class T>
class ThreadPool
{
private:
    // 构造函数 
    ThreadPool(int thread_num = g_thread_num):_num(thread_num)
    {
        pthread_mutex_init(&lock, nullptr);
        pthread_cond_init(&cond, nullptr);

        for(int i = 1; i <= _num; ++i)
        {
            _threads.push_back(new Thread(i, routine, this));
        }
    }
    // 析构函数
    ~ThreadPool()
    {
        for(auto& it : _threads)
        {
            it->join();
            delete it;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }

    // 禁用拷贝 && 赋值
    // 防止多个线程池对象共享同一个线程池内部资源而造成混乱
    ThreadPool(const ThreadPool<T>& other) = delete;
    const ThreadPool<T> &operator=(const ThreadPool<T> &other) = delete;

    

public:
    pthread_mutex_t *getMutex()
    {
        return &lock; // 返回互斥锁指针
    }

    bool isEmpty() // 判断任务队列是否为空
    {
        return _task_queue.empty();
    }

    void waitCond() //等待条件变量
    {
        pthread_cond_wait(&cond, &lock);
    }

    T getTask() // 获取任务
    {
        T t = _task_queue.front(); // 获取队列首位元素（任务）
        _task_queue.pop(); // 删除该任务
        return t;
    }

    // 获取线程池实例
    static ThreadPool<T> *getThreadPool(int num = g_thread_num)
    {
        // 判断是否已经创建了线程池实例
        if(thread_ptr == nullptr)
        {
            // 多个线程可能同时访问 thread_ptr，需要通过互斥锁来保护对其的操作
            lockGuard lockguard(&mutex);

            // 为了避免在多个线程被阻塞等待锁资源时，当第一个线程创建了线程池后，其他线程仍然会创建新的线程池 的情况
            // 再次判断
            if(thread_ptr == nullptr)
            {
                thread_ptr = new ThreadPool<T>(num);
            }   
        }
        return thread_ptr;
    }

    // 启动线程
    void run()
    {
        // 遍历线程池中的每个线程并依次创建
        for(auto& iter : _threads)
        {
            iter->start();
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "Creation completed");
        }    
    }

    // 每个线程的执行函数: 消费者执行逻辑
    static void* routine(void *args)
    {
        ThreadData* td = (ThreadData*)args;
        ThreadPool<T>* tp = (ThreadPool<T>*)td->_args;
        while(true)
        {
            T task;
            {
                // 初始化 task 对象，并调用适当的构造函数
                lockGuard lockguard(tp->getMutex());
                while(tp->isEmpty()) // 当线程为空，消费操作等待
                    tp->waitCond();

                // 读取任务
                task = tp->getTask();
            }
            // 执行任务
            task(td->_name);
        }
    }

    // 添加任务
    void pushTask(const T& task)
    {   
        lockGuard lockguard(&lock);
        _task_queue.push(task); // 将任务传入到 任务队列
        pthread_cond_signal(&cond); // 等待条件变量的线程，有新的任务可用
    }

private:
    std::vector<Thread*> _threads; // 线程集合
    std::queue<T> _task_queue; // 任务队列
    int _num; // 表示线程数量

    static ThreadPool<T>* thread_ptr; // 静态成员指针，用于保存 ThreadPool 类的唯一实例
    static pthread_mutex_t mutex;   // 静态互斥锁，用于保证对 ThreadPool 类唯一实例的访问的互斥性

    pthread_mutex_t lock; // 互斥锁
    pthread_cond_t cond; // 条件变量
};

template <typename T>
ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;

// PTHREAD_MUTEX_INITIALIZER 是一个宏: 用于初始化互斥锁的静态常量。
template <typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;

// 这样的定义和初始化可以确保在使用 ThreadPool<T> 类时， thread_ptr 和 mutex 这两个静态成员变量被正确创建和初始化。
// 静态成员变量是该类所有实例共享的，通过初始化为适当值，确保在使用时具有预期的初始状态。